package com.chungkui.check.configparser;

import com.chungkui.check.configparser.bean.CheckConfig;
import com.chungkui.check.core.Constants;
import com.chungkui.check.expression.ExpressionFactory;
import com.chungkui.check.expression.MatchExpression;
import com.chungkui.check.util.JsonUtil;
import com.google.common.collect.Lists;
import com.googlecode.aviator.AviatorEvaluator;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;

/**
 * @Author: jason
 * @Date: 2020/1/16 10:08
 * @Version 1.0
 */
public class ParsingUtil {
    private static final Logger log = LoggerFactory.getLogger(ParsingUtil.class);

    private ParsingUtil() {
    }

    /**
     * @param checkConfig  配置bean
     * @param localConfig  本地配置
     * @param remoteConfig 远程配置
     */
    public static void packageTrafficControlConfig(CheckConfig checkConfig, String localConfig, String remoteConfig) {
        Map<String, Object> data;
        if (StringUtils.isNotEmpty(remoteConfig)) {
            data = JsonUtil.json2map(remoteConfig);
        } else {
            data = JsonUtil.json2map(localConfig);
        }
        if (MapUtils.isNotEmpty(data)) {
            CheckConfig.TrafficControlConfig trafficControlConfig = checkConfig.new TrafficControlConfig();
            trafficControlConfig.setDynamicParam(MapUtils.getString(data, Constants.PARAM));
            trafficControlConfig.setKey(MapUtils.getString(data, "key"));
            trafficControlConfig.setMsg(MapUtils.getString(data, Constants.MSG));
            trafficControlConfig.setCode(MapUtils.getString(data, "code"));
            checkConfig.setTrafficControlConfig(trafficControlConfig);
        }
    }

    /**
     * @param checkConfig  配置bean
     * @param localConfig  本地配置
     * @param remoteConfig 远程配置
     */
    public static void packageReSubmitWallConfig(CheckConfig checkConfig, String localConfig, String remoteConfig) {
        Map<String, Object> data;
        if (StringUtils.isNotEmpty(remoteConfig)) {
            data = JsonUtil.json2map(remoteConfig);
        } else {
            data = JsonUtil.json2map(localConfig);
        }
        if (MapUtils.isNotEmpty(data)) {
            CheckConfig.ResumitWallConfig resumitWallConfig = checkConfig.new ResumitWallConfig();
            resumitWallConfig.setExpire(MapUtils.getIntValue(data, "expire"));
            resumitWallConfig.setPrefix(MapUtils.getString(data, "prefix"));
            resumitWallConfig.setMsgConfig((Map<String, Object>) MapUtils.getMap(data, Constants.MSG));
            resumitWallConfig.setCode(MapUtils.getString(data, "code"));
            checkConfig.setResumitWallConfig(resumitWallConfig);
        }
    }

    /**
     * @param checkConfig  配置bean
     * @param remoteConfig 远程配置
     */
    public static void parseDemoteConfig(CheckConfig checkConfig, String remoteConfig) {
        Map<String, Object> data = JsonUtil.json2map(remoteConfig);
        if (MapUtils.isNotEmpty(data)) {
            CheckConfig.DemoteConfig demoteConfig = checkConfig.new DemoteConfig();
            demoteConfig.setOpen(MapUtils.getBooleanValue(data, "open"));
            demoteConfig.setMsg(MapUtils.getString(data, Constants.MSG));
            demoteConfig.setMsg(MapUtils.getString(data, "code"));
            checkConfig.setDemoteConfig(demoteConfig);
        }

    }

    private static CheckConfig.RateLimterConfig packageRateLimter(CheckConfig checkConfig, Map<String, Object> data) {
        CheckConfig.RateLimterConfig rateLimterConfig = checkConfig.new RateLimterConfig();
        rateLimterConfig.setDynamicParam(MapUtils.getString(data, "param"));
        rateLimterConfig.setMsg(MapUtils.getString(data, Constants.MSG));
        rateLimterConfig.setCode(MapUtils.getString(data, "code"));
        rateLimterConfig.setPermitsPerSecond(MapUtils.getDoubleValue(data, "rate"));
        rateLimterConfig.setWarmupPeriod(MapUtils.getLong(data, "warmupPeriod"));
        rateLimterConfig.setSync(MapUtils.getBoolean(data, "sync"));
        rateLimterConfig.setWaitLimit(MapUtils.getLong(data, "waitLimit"));
        rateLimterConfig.setCreateRate(MapUtils.getDoubleValue(data, "createRate"));
        return rateLimterConfig;
    }

    private static CheckConfig.RedisRateLimterConfig packageRedisRateLimter(CheckConfig checkConfig, Map<String, Object> data) {
        CheckConfig.RedisRateLimterConfig rateLimterConfig = checkConfig.new RedisRateLimterConfig();
        rateLimterConfig.setDynamicParam(MapUtils.getString(data, "param"));
        rateLimterConfig.setMsg(MapUtils.getString(data, Constants.MSG));
        rateLimterConfig.setCode(MapUtils.getString(data, "code"));
        rateLimterConfig.setCapacity(MapUtils.getInteger(data, "capacity"));
        rateLimterConfig.setRate(MapUtils.getInteger(data, "rate"));
        rateLimterConfig.setPerUser(MapUtils.getInteger(data, "perUser", 1));
        return rateLimterConfig;
    }

    public static void packageCondition(CheckConfig checkConfig, String localConfig, String remoteConfig) {
        if (StringUtils.isNotEmpty(remoteConfig)) {
            checkConfig.setCondition(AviatorEvaluator.compile(remoteConfig));
        } else if (StringUtils.isNotEmpty(localConfig)) {
            checkConfig.setCondition(AviatorEvaluator.compile(localConfig));
        }
    }

    public static void packageRedisRateLimter(CheckConfig checkConfig, String[] localConfig, Object remoteConfig) {
        List<CheckConfig.RedisRateLimterConfig> rtlmt = Lists.newArrayList();
        if (remoteConfig == null) {
            if (localConfig != null) {
                for (int i = 0; i < localConfig.length; i++) {
                    Map<String, Object> limter = JsonUtil.json2map(localConfig[i]);
                    rtlmt.add(packageRedisRateLimter(checkConfig, limter));
                }
            }
        } else {
            List<Map<String, Object>> limters = (List<Map<String, Object>>) remoteConfig;
            if (CollectionUtils.isNotEmpty(limters)) {
                for (Map<String, Object> limter : limters) {
                    rtlmt.add(packageRedisRateLimter(checkConfig, limter));
                }
            }
        }
        if (CollectionUtils.isNotEmpty(rtlmt)) {
            checkConfig.setRedisRateLimterConfigs(rtlmt);
        }
    }

    public static void packageRateLimter(CheckConfig checkConfig, String[] localConfig, Object remoteConfig) {
        List<CheckConfig.RateLimterConfig> rtlmt = Lists.newArrayList();
        if (remoteConfig == null) {
            if (localConfig != null) {
                for (int i = 0; i < localConfig.length; i++) {
                    Map<String, Object> limter = JsonUtil.json2map(localConfig[i]);
                    rtlmt.add(packageRateLimter(checkConfig, limter));
                }
            }
        } else {
            List<Map<String, Object>> limters = (List<Map<String, Object>>) remoteConfig;
            if (CollectionUtils.isNotEmpty(limters)) {
                for (Map<String, Object> limter : limters) {
                    rtlmt.add(packageRateLimter(checkConfig, limter));
                }
            }
        }
        if (CollectionUtils.isNotEmpty(rtlmt)) {
            checkConfig.setRateLimterConfigs(rtlmt);
        }
    }

    public static List<MatchExpression> buildExpressions(List<Map<String, Object>> rules) {
        List<MatchExpression> expressions = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(rules)) {
            for (Map<String, Object> rule : rules) {
                MatchExpression matchExpression = buildExpressions(rule);
                if (matchExpression != null) {
                    expressions.add(matchExpression);
                }
            }
        }
        return expressions;
    }

    public static MatchExpression buildExpressions(Map<String, Object> rule) {
        try {
            return ExpressionFactory.buildExpression(rule);
        } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
            log.error("buildExpressions error", e);
        }
        return null;
    }
}
